/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.sys>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: type_generator_fdbus.cpp
 *
 * Purpose: code generator with fdbus and default serializer/deserializer binding
 *
 * Developer: 
 *   wen.gu , 2021-04-29
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/


#include "code_generator_fdbus.h"
#include <sstream>
#include <map>
#include <fstream>


#include "key_word.h"
#include "file_content.h"
#include "code_generator_utils.h"

#include "log.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/





/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/



class CodeGeneratorFDBus::Impl
{
public:
    std::string header_template_;
    std::string cpp_template_;
};


/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

static inline const std::string& DataType2Str(const DataTypeStrMap2& dtsm, DataType dt)
{
    DataTypeStrMap2::const_iterator it = dtsm.find(dt);

    if (it != dtsm.end())
    {
        return it->second;
    }

    return "";          
}


static inline const std::string& Str2CppType(const DataTypeStrMap& dtsm, const std::string& str)
{
    DataTypeStrMap::const_iterator it = dtsm.find(str);

    if (it != dtsm.end())
    {
        return it->second;
    }

    return "";        
}

static const std::string& DataType2CppType(DataType type)
{
    return DataType2Str(gDataTypeMap, type);
}

static const std::string& BaseType2CppType(const std::string& type)
{
    return Str2CppType(gBaseCppType, type);
}



static void DumpComment(const DataTypeInfo& dti, std::stringstream& code_st)
{
    if (!dti.comment.empty())
    {
        code_st << "/** " << dti.comment << " */\n";        
    }
}

static std::string DumpStruct(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    const DataTypeElements& elements = dti.elements;
    DumpComment(dti, code_st);
    code_st << "struct " << dti.name << "\n{\n";
    
    for (uint32_t i = 0; i < elements.size(); i++)
    {
        const DataTypeElement& dte = elements[i];
        code_st << "    " << dte.type << " " << dte.name << ";\n";
    }

    code_st << "\n};\n\n";

    return std::move(code_st.str());
}

static std::string DumpVector(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    DumpComment(dti, code_st);
    code_st << "using " << dti.name << " = std::vector<" << dti.element_type << ">;\n";

    return std::move(code_st.str());    
}

static std::string DumpEnum(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    const DataTypeElements& elements = dti.elements;
    DumpComment(dti, code_st);
    code_st << "enum class " << dti.name;
    if (!dti.element_type.empty())
    {
        code_st << ": " << dti.element_type;
    }

    code_st << "\n{\n";
    
    for (uint32_t i = 0; i < elements.size(); i++)
    {
        const DataTypeElement& dte = elements[i];
        code_st << "    " << dte.type << " = " << dte.name << ",\n";
    }

    code_st << "\n};\n\n";

    return std::move(code_st.str());
}

static std::string DumpArray(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    DumpComment(dti, code_st);
    code_st << "using " << dti.name << " = " << dti.element_type << "[ "<< dti.elements[0].type << " ];\n\n";

    return std::move(code_st.str());  
}

static std::string DumpMap(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    const DataTypeElements& elements = dti.elements;
    DumpComment(dti, code_st);
    /**
     *  DataTypeElement::type as the key type of map 
     * DataTypeElement::name as the value type of map
     */
    code_st << "using " << dti.name << " = std::map<" << elements[0].type << ", " << elements[0].name << ">;\n\n";

    return std::move(code_st.str());
}

void foo()
{
#if 0    
    if (type_defs_.size() > 0)
    {
        header_files.push_back("string");
        header_files.push_back("stdint.h"); /** default aways include string and stdind.h */

        if (has_vector)
        {
            header_files.push_back("vector");
        }

        if (has_map)
        {
            header_files.push_back("map");
        }

        return true;
    }  
#endif      
}

#if 0
std::string TypeParser::dump()
{
    std::stringstream code_st;
    for (uint32_t i = 0; i < type_defs_.size(); i++)
    {
        DataTypeInfo& dti = type_defs_[i];
        
        switch(dti.type)
        {
        case DataType::kStruct: code_st << DumpStruct(dti); break;
        case DataType::kVector: code_st << DumpVector(dti); break;
        case DataType::kEnum: code_st << DumpEnum(dti); break;
        case DataType::kArray: code_st << DumpArray(dti); break;
        case DataType::kMap: code_st << DumpMap(dti); break;
        default:
            LOGW("unknown composite type: %d\n", (int)dti.type);
            break;
        }
    }

    return std::move(code_st.str());

}

bool TypeParser::dump(std::ofstream& out_file)
{

}

std::string TypeParser::dumpHeaderFile()
{
    std::stringstream code_st;
    if (header_files.size())
    {
        for (size_t i = 0; i < header_files.size(); i++)
        {
            code_st << "#include <" << header_files[i] << ">\n";
            /* code */
        }
        
        return std::move(code_st.str());
    }   

    return ""; 
}
#endif



/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
CodeGeneratorFDBus::CodeGeneratorFDBus()
    :CodeGenerator(GeneratorLanguageType::kCpp, GeneratorCommunicationBinding::kFDBus),
    impl_(new Impl)
{
    /** todo something */
}

CodeGeneratorFDBus::~CodeGeneratorFDBus()
{
    /** todo something */
}


bool CodeGeneratorFDBus::setFileTemplatePath(const std::string& code_template_path)
{
    return ReadTemplateFile(code_template_path + TEMPLATE_HEADER, impl_->header_template_) && 
           ReadTemplateFile(code_template_path + TEMPLATE_SOURCE_CODE, impl_->cpp_template_);
}

bool CodeGeneratorFDBus::generate(const FileContent& file_content, SourceCodeFileArray& out_code)
{

}



